package day30.zqStream;

import java.io.*;
import java.util.Scanner;

/**
 * 测试Java标准流的类
 * 演示System.in、System.out和System.err的使用
 * 
 * Java标准流是Java程序与外部环境进行交互的重要机制，主要包括三个流：
 * 1. System.in - 标准输入流，通常连接到键盘输入
 * 2. System.out - 标准输出流，通常连接到控制台显示
 * 3. System.err - 标准错误流，通常也连接到控制台显示，但可以独立重定向
 * 
 * 这些流在Java程序启动时自动打开，可以直接使用，无需显式创建
 */
public class testStandardStreams {
    /**
     * 程序入口点方法，负责协调调用各个测试方法
     * 
     * @param args 命令行参数数组，用于接收从命令行传入的参数
     *            这是一个String类型的数组，即使没有传入参数也不会为null，而是长度为0的数组
     */
    public static void main(String[] args) {
        // 使用System.out.println方法向标准输出流打印程序开始执行的提示信息
        // println方法会在输出内容后自动添加换行符，使后续输出从新的一行开始
        System.out.println("=== Java标准流测试 ===\n");
        
        // 1. 调用testStandardOutput方法测试标准输出流(System.out)的功能
        // 标准输出流用于输出程序的正常运行结果和提示信息
        testStandardOutput();
        
        // 2. 调用testStandardError方法测试标准错误流(System.err)的功能
        // 标准错误流用于输出程序的错误信息和警告信息，可以与标准输出流独立处理
        testStandardError();
        
        // 3. 调用testStandardInput方法测试标准输入流(System.in)的功能
        // 标准输入流用于接收用户的输入数据，通常是键盘输入
        testStandardInput();
        
        // 4. 调用testStreamRedirection方法测试流重定向功能
        // 流重定向可以将输出流重定向到文件或其他输出目标
        testStreamRedirection();
        
        // 程序所有测试执行完毕，打印结束提示信息
        System.out.println("\n=== 测试完成 ===");
    }
    
    /**
     * 测试标准输出流 (System.out)的各种功能和用法
     * 
     * System.out是PrintStream类的一个实例，提供了多种输出方法：
     * 1. print() - 输出内容但不换行
     * 2. println() - 输出内容并在末尾添加换行符
     * 3. printf() - 格式化输出，类似于C语言的printf函数
     * 
     * 这些方法可以输出各种数据类型，包括基本数据类型和对象类型
     */
    private static void testStandardOutput() {
        // 打印当前测试项目标题
        System.out.println("1. 标准输出流测试:");
        
        // 基本输出测试部分
        // println方法输出内容后会自动换行，光标移动到下一行的开始位置
        System.out.println("  使用println输出一行文本");
        // print方法输出内容后不会换行，光标停留在输出内容的末尾
        System.out.print("  使用print输出不换行文本 ");
        // 由于上一行使用print方法未换行，此行内容会紧接着上一行内容输出
        System.out.println("这一部分在同一行");
        
        // 格式化输出测试部分
        // printf方法支持格式化输出，可以控制输出格式
        // %s表示字符串占位符，%d表示十进制整数占位符，\n表示换行符
        System.out.printf("  使用printf格式化输出: %s今年%d岁\n", "张三", 25);
        
        // 输出不同数据类型的测试部分
        // 通过字符串连接(+)操作符可以将不同数据类型连接成字符串输出
        System.out.println("  输出整数: " + 42);
        // 输出浮点数，Java会自动将double类型转换为字符串
        System.out.println("  输出浮点数: " + 3.14159);
        // 输出布尔值，true会被转换为字符串"true"
        System.out.println("  输出布尔值: " + true);
        // 输出字符，字符'A'会被转换为字符串"A"
        System.out.println("  输出字符: " + 'A');
        // 输出一个空行，用于分隔不同的测试部分
        System.out.println();
    }
    
    /**
     * 测试标准错误流 (System.err)的功能和特点
     * 
     * System.err同样是PrintStream类的实例，与System.out具有相同的方法
     * 但是System.err专门用于输出错误信息，具有以下特点：
     * 1. 默认输出到与System.out相同的设备（通常是控制台）
     * 2. 可以独立于System.out进行重定向
     * 3. 在某些系统中可能以不同颜色显示（如红色）
     * 4. 缓冲区处理方式可能与System.out不同
     */
    private static void testStandardError() {
        // 打印当前测试项目标题
        System.out.println("2. 标准错误流测试:");
        
        // 正常信息输出到标准输出流的示例
        // System.out用于输出程序的正常运行信息和结果
        System.out.println("  这是正常信息，输出到System.out");
        
        // 错误信息输出到标准错误流的示例
        // System.err用于输出程序的错误信息和异常情况
        // 虽然默认情况下两者都显示在控制台上，但可以独立重定向到不同位置
        System.err.println("  这是错误信息，输出到System.err");
        
        // 演示System.out和System.err的区别
        // 通过交替输出可以观察两者的行为差异
        System.out.println("  再次输出正常信息到System.out");
        // 错误信息继续输出到System.err
        System.err.println("  再次输出错误信息到System.err\n");
    }
    
    /**
     * 测试标准输入流 (System.in)的功能
     * 
     * System.in是InputStream类的实例，用于从标准输入设备读取数据
     * 通常标准输入设备是键盘，但也可以重定向为文件或其他数据源
     * 
     * 直接使用System.in比较复杂，通常会包装成更高级的输入流类使用
     * 如Scanner类或BufferedReader类，这些类提供了更方便的读取方法
     */
    private static void testStandardInput() {
        // 打印当前测试项目标题
        System.out.println("3. 标准输入流测试:");
        // 提示用户输入姓名
        System.out.println("  请输入您的姓名: ");
        
        // 使用Scanner类包装System.in，简化输入操作
        // Scanner是Java 5引入的工具类，可以方便地解析基本数据类型和字符串
        Scanner scanner = new Scanner(System.in);
        // 使用nextLine()方法读取用户输入的一整行文本，包括空格
        // nextLine()会一直等待直到用户按下回车键
        String name = scanner.nextLine();
        // 输出用户输入的姓名，验证输入是否正确读取
        System.out.println("  您输入的姓名是: " + name);
        
        // 提示用户输入一个整数
        System.out.println("  请输入一个整数: ");
        // 使用nextInt()方法读取用户输入的整数
        // nextInt()会解析输入的文本并转换为int类型
        int number = scanner.nextInt();
        // 输出用户输入的整数，验证输入是否正确读取
        System.out.println("  您输入的整数是: " + number);
        // 输出一个空行，用于分隔不同的测试部分
        System.out.println();
    }
    
    /**
     * 测试流重定向功能
     * 
     * 流重定向是Java I/O系统的重要特性，允许程序在运行时改变标准流的目标
     * 通过System.setIn()、System.setOut()和System.setErr()方法可以实现重定向
     * 
     * 流重定向的常见用途：
     * 1. 将程序输出保存到文件而不是显示在控制台
     * 2. 从文件读取输入而不是从键盘输入
     * 3. 将错误信息和正常信息输出到不同位置
     * 
     * 注意事项：
     * 1. 重定向后应及时恢复原始流，避免影响其他部分的执行
     * 2. 使用完文件流后需要正确关闭，释放系统资源
     * 3. 文件操作可能抛出IOException，需要进行异常处理
     */
    private static void testStreamRedirection() {
        // 打印当前测试项目标题
        System.out.println("4. 流重定向测试:");
        
        // 使用try-catch语句处理可能发生的IOException
        // 文件操作是I/O操作，可能因为权限、磁盘空间等原因失败
        try {
            // 保存原始的System.out引用，以便后续恢复
            // 在重定向System.out之前必须保存原始引用，否则无法恢复
            PrintStream originalOut = System.out;
            
            // 创建文件输出流，将数据写入到output.txt文件中
            // FileOutputStream是字节流，用于向文件写入字节数据
            // 如果文件不存在会自动创建，如果文件存在会覆盖原有内容
            FileOutputStream fileOut = new FileOutputStream("output.txt");
            // 使用PrintStream包装FileOutputStream，获得与System.out相同的打印功能
            // PrintStream提供了print、println、printf等方便的打印方法
            PrintStream printStream = new PrintStream(fileOut);
            
            // 重定向System.out到文件输出流
            // 调用System.setOut()方法将System.out指向新创建的PrintStream
            // 从此之后所有System.out的输出都会写入到output.txt文件中
            System.setOut(printStream);
            // 这条信息不会显示在控制台，而是写入到output.txt文件中
            System.out.println("这条信息将被写入到output.txt文件中");
            // 这条信息同样写入到output.txt文件中
            System.out.println("这是第二条写入文件的信息");
            
            // 恢复原始的System.out，使后续输出重新显示在控制台
            // 调用System.setOut()方法将System.out指回原始的PrintStream
            System.setOut(originalOut);
            // 这条信息会显示在控制台，因为System.out已经恢复
            System.out.println("  流重定向测试完成，信息已写入output.txt文件");
            
            // 关闭文件流，释放相关系统资源
            // 虽然Java有垃圾回收机制，但显式关闭流是良好的编程习惯
            printStream.close();
            // 关闭底层的文件输出流
            fileOut.close();
            
        } catch (IOException e) {
            // 捕获并处理文件操作过程中可能发生的异常
            // 使用System.err输出错误信息，符合错误处理的最佳实践
            System.err.println("  流重定向过程中发生错误: " + e.getMessage());
        }
        
        // 演示从文件读取之前写入的内容
        System.out.println("  从文件读取内容:");
        // 使用try-with-resources语句自动管理资源
        // 括号中的资源会在try语句结束后自动关闭，无需手动调用close()
        try (BufferedReader reader = new BufferedReader(new FileReader("output.txt"))) {
            // 声明一个字符串变量用于存储从文件读取的每一行
            String line;
            // 使用while循环逐行读取文件内容
            // readLine()方法每次返回一行文本，文件结束时返回null
            while ((line = reader.readLine()) != null) {
                // 将读取到的每一行内容输出到控制台，前面添加缩进空格
                System.out.println("    " + line);
            }
        } catch (IOException e) {
            // 捕获并处理文件读取过程中可能发生的异常
            System.err.println("  读取文件时发生错误: " + e.getMessage());
        }
    }
}